/**
 *
 */
package com.sihai.core.web.struts2.interceptor;

import org.hibernate.FlushMode;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.orm.hibernate3.SessionFactoryUtils;
import org.springframework.orm.hibernate3.SessionHolder;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.Interceptor;
import com.sihai.core.beans.BeanLocator;

/**
 * @author pippo
 *
 */
public class OpenSessionInViewInterceptor implements Interceptor {

	/**
	 * serialVersionUID
	 */
	private static final long serialVersionUID = -3686422152109258828L;

	private static Logger logger = LoggerFactory.getLogger(OpenSessionInViewInterceptor.class);

	public static final String DEFAULT_SESSION_FACTORY_BEAN_NAME = "sessionFactory";

	private boolean singleSession = true;

	private FlushMode flushMode = FlushMode.MANUAL;

	private SessionFactory sessionFactory;

	@Override
	public void destroy() {

	}

	@Override
	public void init() {
		sessionFactory = (SessionFactory) BeanLocator.getBean(DEFAULT_SESSION_FACTORY_BEAN_NAME);
	}

	@Override
	public String intercept(ActionInvocation invocation) throws Exception {
		boolean participate = false;

		if (singleSession) {
			// single session mode
			if (TransactionSynchronizationManager.hasResource(sessionFactory)) {
				// Do not modify the Session: just set the participate flag.
				participate = true;
			} else {
				if (logger.isDebugEnabled())
					logger.debug("Opening single Hibernate Session in OpenSessionInViewInterceptor");
				Session session = getSession(sessionFactory);
				TransactionSynchronizationManager.bindResource(sessionFactory,
						new SessionHolder(session));
			}
		} else {
			// deferred close mode
			if (SessionFactoryUtils.isDeferredCloseActive(sessionFactory)) {
				// Do not modify deferred close: just set the participate flag.
				participate = true;
			} else {
				SessionFactoryUtils.initDeferredClose(sessionFactory);
			}
		}

		try {
			return invocation.invoke();
		}

		finally {
			if (!participate) {
				if (isSingleSession()) {
					// single session mode
					SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager
							.unbindResource(sessionFactory);
					if (logger.isDebugEnabled())
						logger.debug("Closing single Hibernate Session in OpenSessionInViewInterceptor");
					closeSession(sessionHolder.getSession(), sessionFactory);
				} else {
					// deferred close mode
					SessionFactoryUtils.processDeferredClose(sessionFactory);
				}
			}
		}

	}

	protected Session getSession(SessionFactory sessionFactory)
			throws DataAccessResourceFailureException {
		Session session = SessionFactoryUtils.getSession(sessionFactory, true);
		FlushMode flushMode = getFlushMode();
		if (flushMode != null) {
			session.setFlushMode(flushMode);
		}
		return session;
	}

	protected void closeSession(Session session, SessionFactory sessionFactory) {
		SessionFactoryUtils.closeSession(session);
	}

	public void setSingleSession(boolean singleSession) {
		this.singleSession = singleSession;
	}

	protected boolean isSingleSession() {
		return this.singleSession;
	}

	public void setFlushMode(FlushMode flushMode) {
		this.flushMode = flushMode;
	}

	protected FlushMode getFlushMode() {
		return this.flushMode;
	}

}
